/******************************************************************************
秋夜静，独坐对残灯。
啼笑非非谁识我，坐行梦梦尽缘君，何所慰消沉。
风卷雨，雨复卷侬心。
心似欲随风雨去，茫茫大海任浮沉。
无爱亦无恨。
******************************************************************************/
#include "kbRGB.h"
#include <math.h>

#define RGBGetRValue(rgb)   (rgb & 0x000000FF)
#define RGBGetGValue(rgb)   ((rgb >> 8) & 0x000000FF)
#define RGBGetBValue(rgb)   ((rgb >> 16) & 0x000000FF)

#define ToRGBColor(r, g, b) ((uint32_t)((b << 16) | (g << 8) | (r)))
static unsigned char ScreenChanel(unsigned char a, unsigned char b)
{
    return 255 - ((255 - b) * (255 - a) >> 8);;
}
static uint32_t Screen(uint32_t color1, uint32_t color2)
{
    return ToRGBColor(
                ScreenChanel(RGBGetRValue(color1), RGBGetRValue(color2)),
                ScreenChanel(RGBGetGValue(color1), RGBGetGValue(color2)),
                ScreenChanel(RGBGetBValue(color1), RGBGetBValue(color2))
                );
};
static unsigned char InterpolateChanel(unsigned char a, unsigned char b, float x)
{
    return (int) ((b - a) * x + a);
}
static uint32_t Interpolate(uint32_t color1, uint32_t color2, float fraction)
{
    return ToRGBColor(
                InterpolateChanel(RGBGetRValue(color1), RGBGetRValue(color2), fraction),
                InterpolateChanel(RGBGetGValue(color1), RGBGetGValue(color2), fraction),
                InterpolateChanel(RGBGetBValue(color1), RGBGetBValue(color2), fraction)
                );
}

/*************************************************************************************************\
|        Beams [9]                                                                                |
\*************************************************************************************************/
static void SetRandomColors(void)
{
    kbrgb_Params.Beams.hsv1.hue = 0;
    kbrgb_Params.Beams.hsv1.value = 255;
    kbrgb_Params.Beams.hsv1.saturation = 255;
    kbrgb_Params.Beams.hsv2.hue = 180;
    kbrgb_Params.Beams.hsv2.value = 255;
    kbrgb_Params.Beams.hsv2.saturation = 255;
}
//static hsv_t hsv1;
//static hsv_t hsv2;
//static double sine_x = 0;
//static double sine_y = 0;
//static int v_speed = 10;
//static int h_speed = 15;
//static int glow = 30;
//static int thickness = 1;
#if 0
static void CrossingBeams_SetRandomColorsEnabled(int RandomColorsEnabled)
{
    //RandomColorsEnabled = value;

//    if(!RandomColorsEnabled)
//    {
//        //ResetUserColors();
//        rgb2hsv(UserColors[0], &hsv1);
//        rgb2hsv(UserColors[1], &hsv2);
//    }
//    else
    {
        kbrgb_Params.Beams.hsv1.hue = 0;
        kbrgb_Params.Beams.hsv1.value = 255;
        kbrgb_Params.Beams.hsv1.saturation = 255;

        kbrgb_Params.Beams.hsv2.hue = 180;
        kbrgb_Params.Beams.hsv2.value = 255;
        kbrgb_Params.Beams.hsv2.saturation = 255;
    }
}
#endif
void kbRGB_CrossingBeams_Init(void)    // 交叉梁   // Fail
{
    kbrgb_Params.Speed = 15;
    kbrgb_Params.Slider2Val = 30;
    kbrgb_Params.Beams.v_speed = 10;
    kbrgb_Params.Beams.h_speed = 15;
    kbrgb_Params.Beams.glow = 30;
    kbrgb_Params.Beams.thickness = 1;
    //CrossingBeams_SetRandomColorsEnabled(1);
    SetRandomColors();
}
static uint32_t CrossingBeams_GetColor(const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h, const float x_progress, const float y_progress)
{

//    float x_progress = 0.5 * (1 + sine_x) * w;
//    float y_progress = 0.5 * (1 + sine_y) * h;
//    float x_progress = 0.5 * (kbRGB_SinMax + sine_x) * w/kbRGB_SinMax;
//    float y_progress = 0.5 * (kbRGB_SinMax + sine_y) * h/kbRGB_SinMax;
    //int16_t x_progress = ((255 + sine_x) * w)>>(KBRGB_SIN_CAL_PRE+1);
    //int16_t y_progress = ((255 + sine_y) * h)>>(KBRGB_SIN_CAL_PRE+1);

    float distance_x = fabs(x_progress - x);
    //float distance_x_percent = std::min<float>(1,pow(distance_x / w,
    //                                                 distance_x > thickness ? 0.01 * glow : 1));
    float distance_x_percent = pow(distance_x / w, distance_x > kbrgb_Params.Beams.thickness ? 0.01 * kbrgb_Params.Beams.glow : 1);
    if(distance_x_percent>1) distance_x_percent=1;

    float distance_y = fabs(y_progress - y);
    //float distance_y_percent = std::min<float>(1, pow(distance_y / h,
    //                                                  distance_y > thickness ? 0.01 * glow : 1));
    float distance_y_percent = pow(distance_y / h, distance_y > kbrgb_Params.Beams.thickness ? 0.01 * kbrgb_Params.Beams.glow : 1);
    if(distance_y_percent>1) distance_y_percent=1;

    hsv_t hsv_tmp;

    hsv_tmp.value = kbrgb_Params.Beams.hsv1.value * (1 - distance_x_percent);
    hsv_tmp.hue = kbrgb_Params.Beams.hsv1.hue;
    hsv_tmp.saturation = kbrgb_Params.Beams.hsv1.saturation;

    uint32_t color1 = (hsv2rgb(&hsv_tmp));

    hsv_tmp.value = kbrgb_Params.Beams.hsv2.value * (1 - distance_y_percent);
    hsv_tmp.hue = kbrgb_Params.Beams.hsv2.hue;
    hsv_tmp.saturation = kbrgb_Params.Beams.hsv2.saturation;

    uint32_t color2 = (hsv2rgb(&hsv_tmp));

    return Screen(color1,color2);
}
void kbRGB_CrossingBeams(const uint16_t cols, const uint16_t rows)    // 交叉梁   // Fail
{
//    double sine_x = sin( 0.01 * kbrgb_Params.Beams.h_speed * kbRGB_progress);
//    double sine_y = sin( 0.01 * kbrgb_Params.Beams.v_speed * kbRGB_progress);
    int16_t sine_x = kbRGB_Sin( (kbrgb_Params.Beams.h_speed * kbRGB_progress)*1.80/KBRGB_PI);
    int16_t sine_y = kbRGB_Sin( (kbrgb_Params.Beams.v_speed * kbRGB_progress)*1.80/KBRGB_PI);
    //    float x_progress = 0.5 * (kbRGB_SinMax + sine_x) * w/kbRGB_SinMax;
    //    float y_progress = 0.5 * (kbRGB_SinMax + sine_y) * h/kbRGB_SinMax;
    float x_progress = 0.5 * (kbRGB_SinMax + sine_x) * cols/kbRGB_SinMax;
    float y_progress = 0.5 * (kbRGB_SinMax + sine_y) * rows/kbRGB_SinMax;
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = CrossingBeams_GetColor(col_id, row_id, cols, rows, x_progress, y_progress);
            kbRGB_pset(col_id, row_id, color);
        }
    }
    kbRGB_progress += (float) kbRGB_Speed / (float) kbRGB_FPS;

    kbrgb_Params.Beams.hsv1.hue++;
    kbrgb_Params.Beams.hsv2.hue++;
}

/*************************************************************************************************\
|        RotatingBeam [9]                                                                              |
\*************************************************************************************************/
struct Line{
    struct{
        float x,y;
        //uint8_t x,y;
    }p1,p2;
    double _sqrt;
};
enum RotatingBeamMode
{
    CLOCKWISE = 0,
    COUNTER_CLOCKWISE = 1,
    PENDULUM = 2,
    WIPERS = 3,
    SWING_H = 4,
    SWING_V = 5
};
//static enum RotatingBeamMode mode = CLOCKWISE;//CLOCKWISE;
#if 0
static void RotatingBeamRemodel_SetRandomColorsEnabled(int RandomColorsEnabled)
{
//    RandomColorsEnabled = value;

//    if(!RandomColorsEnabled)
//    {
//        ResetUserColors();
//    }
//    else
    {
        kbrgb_Params.Beams.hsv1.hue = 0;
        kbrgb_Params.Beams.hsv1.value = 255;
        kbrgb_Params.Beams.hsv1.saturation = 255;

        kbrgb_Params.Beams.hsv2.hue = 180;
        kbrgb_Params.Beams.hsv2.value = 255;
        kbrgb_Params.Beams.hsv2.saturation = 255;
    }
}
#endif
void kbRGB_RotatingBeam_Init(void)    // 旋转射束
{
    kbrgb_Params.Speed = 50;
    kbrgb_Params.Slider2Val = 10;
    kbrgb_Params.Beams.thickness = 0;
    kbrgb_Params.Beams.v_speed = 10;
    kbrgb_Params.Beams.h_speed = 15;
    kbrgb_Params.Beams.glow = 30;
    kbrgb_Params.Beams.thickness = 1;
    kbrgb_Params.Beams.RotatingMode = PENDULUM;//CLOCKWISE;
    //RotatingBeamRemodel_SetRandomColorsEnabled(1);
    SetRandomColors();
}
#if 0
static void RotatingBeamRemodel_Rotate(struct Line* const line)
{
    //struct Line line;

    switch (kbrgb_Params.Beams.RotatingMode)
    {
    case CLOCKWISE:
    {
        float x = 0.5 * (1 + cos(kbrgb_Params.progress));
        float y = 0.5 * (1 + sin(kbrgb_Params.progress));

        line->p1.x=(x);
        line->p1.y=(y);
        line->p2.x=(1 - x);
        line->p2.y=(1 - y);

        break;

    }
    case COUNTER_CLOCKWISE:
    {
        float x = 0.5 * (1 + cos(-kbrgb_Params.progress));
        float y = 0.5 * (1 + sin(-kbrgb_Params.progress));

        line->p1.x=(x);
        line->p1.y=(y);
        line->p2.x=(1 - x);
        line->p2.y=(1 - y);

        break;

    }
    case PENDULUM:
    {
        float x = 0.5 * (1 + cos(kbrgb_Params.progress));

        line->p1.x=(0.5);
        line->p1.y=(0);
        line->p2.x=(x);
        line->p2.y=(1);

        break;

    }
    case WIPERS:
    {
        float x = 0.5 * (1 + cos(kbrgb_Params.progress));

        line->p1.x=(x);
        line->p1.y=(0);
        line->p2.x=(0.5);
        line->p2.y=(1);

        break;

    }
    case SWING_H:
    {
        float x = 0.5 * (1 + cos(kbrgb_Params.progress));

        line->p1.x=(0);
        line->p1.y=(x);
        line->p2.x=(1);
        line->p2.y=(1 - x);

        break;

    }
    case SWING_V:
    //default:
    {
        float x = 0.5 * (1 + cos(kbrgb_Params.progress));

        line->p1.x=(x);
        line->p1.y=(0);
        line->p2.x=(1 - x);
        line->p2.y=(1);

        break;

    }
    default: ;//return QLineF({0, 0}, {1, 1});
        line->p1.x=0;
        line->p1.y=0;
        line->p2.x=1;
        line->p2.y=1;
    }

    //return QLineF(p1, p2);
}
#else
static void RotatingBeamRemodel_Rotate(struct Line* const line)
{
    //struct Line line;

    const int32_t angle = kbRGB_progress*180/KBRGB_PI;
    switch (kbrgb_Params.Beams.RotatingMode)
    {
    case CLOCKWISE:
    {
//        int16_t x = 0.5 * (kbRGB_SinMax + kbRGB_Cos(angle));
//        int16_t y = 0.5 * (kbRGB_SinMax + kbRGB_Sin(angle));
        int16_t x = (kbRGB_SinMax + kbRGB_Cos(angle))>>1;
        int16_t y = (kbRGB_SinMax + kbRGB_Sin(angle))>>1;

        line->p1.x=(x);
        line->p1.y=(y);
        line->p2.x=(kbRGB_SinMax - x);
        line->p2.y=(kbRGB_SinMax - y);

        break;

    }
    case COUNTER_CLOCKWISE:
    {
//        float x = 0.5 * (kbRGB_SinMax + kbRGB_Cos(-angle));
//        float y = 0.5 * (kbRGB_SinMax + kbRGB_Sin(-angle));
        int16_t x = (kbRGB_SinMax + kbRGB_Cos(-angle))>>1;
        int16_t y = (kbRGB_SinMax + kbRGB_Sin(-angle))>>1;

        line->p1.x=(x);
        line->p1.y=(y);
        line->p2.x=(kbRGB_SinMax - x);
        line->p2.y=(kbRGB_SinMax - y);

        break;

    }
    case PENDULUM:
    {
//        float x = 0.5 * (kbRGB_SinMax + kbRGB_Cos(angle));
        int16_t x = (kbRGB_SinMax + kbRGB_Cos(angle))>>1;

        line->p1.x=(kbRGB_SinMax>>1)+1;
        line->p1.y=(0);
        line->p2.x=(x);
        line->p2.y=(kbRGB_SinMax);

        break;

    }
    case WIPERS:
    {
        //float x = 0.5 * (1 + kbRGB_Cos(angle));
        int16_t x = (kbRGB_SinMax + kbRGB_Cos(angle))>>1;

        line->p1.x=(x);
        line->p1.y=(0);
        line->p2.x=(kbRGB_SinMax>>1)+1;
        line->p2.y=(kbRGB_SinMax);

        break;

    }
    case SWING_H:
    {
        //float x = 0.5 * (1 + kbRGB_Cos(angle));
        int16_t x = (kbRGB_SinMax + kbRGB_Cos(angle))>>1;

        line->p1.x=(0);
        line->p1.y=(x);
        line->p2.x=(kbRGB_SinMax);
        line->p2.y=(kbRGB_SinMax - x);

        break;

    }
    case SWING_V:
    //default:
    {
        //float x = 0.5 * (1 + kbRGB_Cos(angle));
        int16_t x = (kbRGB_SinMax + kbRGB_Cos(angle))>>1;

        line->p1.x=(x);
        line->p1.y=(0);
        line->p2.x=(kbRGB_SinMax - x);
        line->p2.y=(kbRGB_SinMax);

        break;

    }
    default: ;//return QLineF({0, 0}, {1, 1});
        line->p1.x=0;
        line->p1.y=0;
        line->p2.x=kbRGB_SinMax;
        line->p2.y=kbRGB_SinMax;
    }
//    line->p1.x/=(float)kbRGB_SinMax;
//    line->p1.y/=(float)kbRGB_SinMax;
//    line->p2.x/=(float)kbRGB_SinMax;
//    line->p2.y/=(float)kbRGB_SinMax;

    //return QLineF(p1, p2);
}
#endif
static uint32_t RotatingBeamRemodel_GetColor(const uint16_t x0, const uint16_t y0, struct Line* const l, const uint16_t w, const uint16_t h)
{
    //float glow = 0.01 * kbrgb_Params.Slider2Val;
#if 0
    float x1 = l->p1.x * w;
    float x2 = l->p2.x * w;
    float y1 = l->p1.y * h;
    float y2 = l->p2.y * h;
#else
    const float x1 = l->p1.x;
    const float x2 = l->p2.x;
    const float y1 = l->p1.y;
    const float y2 = l->p2.y;
#endif

    //float distance = fabs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / sqrt(pow(x2 - x1 ,2) + pow(y2 - y1,2));
    float distance = fabs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / l->_sqrt;

    hsv_t hsv_tmp;

    hsv_tmp.value = kbrgb_Params.Beams.hsv1.value - kbrgb_Params.Beams.hsv1.value * pow(distance / (0.5*(w+h)) , distance < kbrgb_Params.Beams.thickness ? 1 : kbrgb_Params.Beams.glow);
    hsv_tmp.hue = kbrgb_Params.Beams.hsv1.hue;
    hsv_tmp.saturation = kbrgb_Params.Beams.hsv1.saturation;

    uint32_t color1 = (hsv2rgb(&hsv_tmp));

    uint32_t color2 = (hsv2rgb(&kbrgb_Params.Beams.hsv2));

    return Interpolate(color2, color1, 1 - distance / (0.5*(w+h)) );
}
void kbRGB_RotatingBeam(const uint16_t cols, const uint16_t rows)    // 旋转射束  // 有效果,但不稳定
{
    struct Line line; RotatingBeamRemodel_Rotate(&line);
    const uint16_t width = cols;
    const uint16_t height = rows;
    line.p1.x = line.p1.x * width/(float)kbRGB_SinMax;
    line.p2.x = line.p2.x * width/(float)kbRGB_SinMax;
    line.p1.y = line.p1.y * height/(float)kbRGB_SinMax;
    line.p2.y = line.p2.y * height/(float)kbRGB_SinMax;
    //loat distance = fabs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / sqrt(pow(x2 - x1 ,2) + pow(y2 - y1,2));
    line._sqrt = sqrt(pow(line.p2.x - line.p1.x ,2) + pow(line.p2.y - line.p1.y,2));
    for(uint16_t row_id = 0; row_id < height; row_id++)
    {
        for(uint16_t col_id = 0; col_id <  width; col_id++)
        {
            uint32_t color = RotatingBeamRemodel_GetColor(col_id, row_id, &line, width - 1, height - 1);
            kbRGB_pset(col_id, row_id, color);
        }
    }
    kbRGB_progress +=  0.1 * (float) kbRGB_Speed / (float) kbRGB_FPS;

    kbrgb_Params.Beams.hsv1.hue++;
    kbrgb_Params.Beams.hsv2.hue++;
}
